#!/usr/bin/env python
"""
Acorn-SDR (A Collaboration of Radio Nodes)
(c) Bob Cowdery (G3UKB) 2009

File: dsp_service.py
Date: January 2009

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

The author can be reached by email at

bob@g3ukb.co.uk

================================================================================
Module Description:

The interface service for HPSDR Ozy.

"""

#-------------------------------------------------------------------------------
# System imports
import sys
import os
from types import *
from time import sleep
import Ice, IceStorm

# Application imports
import ozy
import ice_helpers
import utils

# Import the Ice interface definitions
# Common 
import AcornLifeCycle
import AcornPersist
# Rx side
import AcornRxUpdate
import AcornRxDsp
import AcornIqIn
import AcornRxOut
import AcornRxControl
#Tx side
import AcornTxUpdate
import AcornTxDsp
import AcornMicIn
import AcornIqOut
import AcornTxControl

#===============================================================================
# Ice life-cycle events for the GUI
class LifeCycleI(AcornLifeCycle.LifeCycle):
    
    #---------------------------------------------------------------------------   
    def __init__(self, service, ozy_impl):
        self.ozy_impl = ozy_impl
        self.service = service
      
    #---------------------------------------------------------------------------
    def start(self, current=None):
        self.ozy_impl.power_on()
     
    #---------------------------------------------------------------------------   
    def stop(self, current=None):
        self.ozy_impl.power_off()
    
    #---------------------------------------------------------------------------    
    def terminate(self, current=None):
        self.service.terminate()
        
#===============================================================================
# RX Interfaces for HPSDR
#===============================================================================
# Ice command events for the Ozy Controller
class RxControlI(AcornRxControl.RxControl):
    
    #---------------------------------------------------------------------------   
    def __init__(self, service, ozy_impl):
      self.ozy_impl = ozy_impl
      self.service = service
        
    #---------------------------------------------------------------------------   
    def freqSet(self, freq, current=None):
        self.ozy_impl.freq_set(freq)
        
    #---------------------------------------------------------------------------   
    def freqInc(self, inc, current=None):
        self.ozy_impl.freq_inc(inc)
               
#===============================================================================
# Ice receiver for the audio stream
# This outputs the stream to Ozy RX from the DSP
class RxOutI(AcornRxOut.RxOut):
    
    #---------------------------------------------------------------------------
    def __init__(self, ozy_impl):
      self.ozy_impl = ozy_impl
    
    #---------------------------------------------------------------------------   
    def data(self, samples, current=None):
        self.ozy_impl.output(samples)

#===============================================================================
# Ice RX config events for Ozy
class RxConfigI(AcornRxUpdate.RxConfig):
    
    #---------------------------------------------------------------------------   
    def __init__(self, ozy_impl, persist, config_map):
        self.ozy_impl = ozy_impl
        self.persist = persist
        self.map = config_map
      
    #---------------------------------------------------------------------------   
    def configUpdate(self, name, current=None):
        # Update the Ozy configuration
        if name in self.map:
            # Its something we are interested in so go get it
            item = utils.persist_get(self.persist, 'Profile', name)
            # and pass it in
            self.map[name](item)
            
#===============================================================================
# TX Interfaces for HPSDR
#===============================================================================
# Ice command events for the Ozy Controller
class TxControlI(AcornTxControl.TxControl):
    
    #---------------------------------------------------------------------------   
    def __init__(self, service, ozy_impl):
      self.ozy_impl = ozy_impl
      self.service = service
        
    #---------------------------------------------------------------------------   
    def freqSet(self, freq, current=None):
        self.ozy_impl.freq_set(freq)
        
    #---------------------------------------------------------------------------   
    def freqInc(self, inc, current=None):
        self.ozy_impl.freq_inc(inc)
    
    #---------------------------------------------------------------------------   
    def mox(self, state, current=None):
        pass
        #self.ozy_impl.mox(state)
               
#===============================================================================
# Ice receiver for the audio stream
# This outputs the stream to Ozy TX from the DSP
class IqOutI(AcornIqOut.IqOut):
    
    #---------------------------------------------------------------------------
    def __init__(self, ozy_impl):
      self.ozy_impl = ozy_impl
    
    #---------------------------------------------------------------------------   
    def data(self, samples, current=None):
        self.ozy_impl.output(samples)

#===============================================================================
# Ice RX config events for Ozy
class TxConfigI(AcornTxUpdate.TxConfig):
    
    #---------------------------------------------------------------------------   
    def __init__(self, ozy_impl, persist, config_map):
        self.ozy_impl = ozy_impl
        self.persist = persist
        self.map = config_map
      
    #---------------------------------------------------------------------------   
    def configUpdate(self, name, current=None):
        # Update the Ozy configuration
        if name in self.map:
            # Its something we are interested in so go get it
            item = utils.persist_get(self.persist, 'Profile', name)
            # and pass it in
            self.map[name](item)
            
#===============================================================================
#===============================================================================
class OzyService(Ice.Application):
    
    #---------------------------------------------------------------------------
    def __init__(self):       
        self.close = False
    
    #---------------------------------------------------------------------------
    # Run the app
    def run(self, args):
       
       #-----------------------------------------------------------------------
        # persist interface
        persist = ice_helpers.get_interface('Ozy Service', self.communicator, AcornPersist.PersistPrx, 'Persist.Proxy')
        # Get config
        frames = utils.persist_get(persist, 'Profile', 'audio.frames')
        rate = utils.persist_get(persist, 'Profile', 'audio.rate')
        ref = utils.persist_get(persist, 'Profile', 'hpsdr.ref')
        src = utils.persist_get(persist, 'Profile', 'hpsdr.src')
        config = utils.persist_get(persist, 'Profile', 'hpsdr.config')
        mic = utils.persist_get(persist, 'Profile', 'hpsdr.mic')
        
        #-----------------------------------------------------------------------
        # Event names used here
        # Common
        subscribe_to_lc = 'life_cycle_events'
        # RX events
        publish_to_rx_updates = 'rx_update_events'
        subscribe_to_rx_config = 'rx_config_events'
        subscribe_to_rx_control = 'rx_control_events'
        # TX events
        publish_to_tx_updates = 'tx_update_events'
        subscribe_to_tx_config = 'tx_config_events'
        subscribe_to_tx_control = 'tx_control_events'
  
        #-----------------------------------------------------------------------
        # This is the same for both publishers and subscribers
        # Get the IceStorm topic manager interface
        manager = ice_helpers.get_topic_manager('Ozy Service', self.communicator)
        
        # Retrieve the subscriber topic
        # Common
        life_cycle_topic = ice_helpers.get_topic('Ozy Service', manager, subscribe_to_lc)
        # RX topics
        rx_control_topic = ice_helpers.get_topic('Ozy Service', manager, subscribe_to_rx_control)
        rx_config_topic = ice_helpers.get_topic('Ozy Service', manager, subscribe_to_rx_config)
        # TX topics
        tx_control_topic = ice_helpers.get_topic('Ozy Service', manager, subscribe_to_tx_control)
        tx_config_topic = ice_helpers.get_topic('Ozy Service', manager, subscribe_to_tx_config)
        
        # Retrieve the publisher topics
        # RX topics
        publisher_rx_update_topic = ice_helpers.get_topic('Ozy Service', manager, publish_to_rx_updates)
        # TX topics
        publisher_tx_update_topic = ice_helpers.get_topic('Ozy Service', manager, publish_to_tx_updates)
  
        #-----------------------------------------------------------------------
        # Publisher side
        # Get the topic's publisher object, and create an update proxy
        # RX side
        rx_update_publisher = publisher_rx_update_topic.getPublisher();
        rx_update_publisher = rx_update_publisher.ice_oneway();        
        rx_update_publisher = AcornRxUpdate.RxUpdatePrx.uncheckedCast(rx_update_publisher)
        # TX side
        tx_update_publisher = publisher_tx_update_topic.getPublisher();
        tx_update_publisher = tx_update_publisher.ice_oneway();        
        tx_update_publisher = AcornTxUpdate.TxUpdatePrx.uncheckedCast(tx_update_publisher)
        
        #-----------------------------------------------------------------------
        # Initialise the Ozy as its needed for the adapter and requires the update publisher
        ozy_impl = ozy.Ozy(persist, rx_update_publisher, tx_update_publisher, ref, src, config, mic, frames, rate)
        config_map = {'audio.frames': ozy_impl.set_audioframes, 'audio.rate': ozy_impl.set_audiorate}
        
        #-----------------------------------------------------------------------
        # Subscriber side
        adapter_1 = self.communicator().createObjectAdapter("LifeCycle.Subscriber")
        subId_1 = Ice.Identity()
        subId_1.name = Ice.generateUUID()        
        # Add the update interface
        subscriber_1 = adapter_1.add(LifeCycleI(self, ozy_impl), subId_1)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Ozy Service', life_cycle_topic, subscriber_1, id)
        # Now we can receive callbacks   
        adapter_1.activate()
        
        # RX side
        # Create an adapter for the subscriber rx control interface
        adapter_2 = self.communicator().createObjectAdapter("RxControl.Subscriber")
        subId_2 = Ice.Identity()
        subId_2.name = Ice.generateUUID()      
        # Add the update interface
        subscriber_2 = adapter_2.add(RxControlI(self, ozy_impl), subId_2)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Ozy Service', rx_control_topic, subscriber_2, id)
        # Now we can receive callbacks   
        adapter_2.activate()
        # and config
        adapter_3 = self.communicator().createObjectAdapter("RxConfig.Subscriber")
        subId_3 = Ice.Identity()
        subId_3.name = Ice.generateUUID()        
        # Add the update interface
        subscriber_3 = adapter_3.add(RxConfigI(ozy_impl, persist, config_map), subId_3)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Ozy Service', rx_config_topic, subscriber_3, id)
        # Now we can receive callbacks   
        adapter_3.activate()
        
        # TX side
        # Create an adapter for the subscriber tx control interface
        adapter_4 = self.communicator().createObjectAdapter("TxControl.Subscriber")
        subId_4 = Ice.Identity()
        subId_4.name = Ice.generateUUID()      
        # Add the update interface
        subscriber_4 = adapter_4.add(RxControlI(self, ozy_impl), subId_4)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Ozy Service', rx_control_topic, subscriber_4, id)
        # Now we can receive callbacks   
        adapter_4.activate()
        # and config
        adapter_5 = self.communicator().createObjectAdapter("TxConfig.Subscriber")
        subId_5 = Ice.Identity()
        subId_5.name = Ice.generateUUID()        
        # Add the update interface
        subscriber_5 = adapter_5.add(TxConfigI(ozy_impl, persist, config_map), subId_5)
        # Subscribe to the topic - set up proxy
        ice_helpers.subscribe('Ozy Service', tx_config_topic, subscriber_5, id)
        # Now we can receive callbacks   
        adapter_5.activate()
        
        #-----------------------------------------------------------------------
        # One way audio channel in
        # DSP out to Ozy RX out
        audio_adapter_1 = self.communicator().createObjectAdapter("OzyOut")
        #audio_adapter_1 = self.communicator().createObjectAdapter("StreamOut")
        audio_adapter_1.add(RxOutI(ozy_impl), self.communicator().stringToIdentity("rxout"))       
        # Now we can receive callbacks for rx audio 
        audio_adapter_1.activate()
        
        # DSP out to Ozy TX out
        audio_adapter_2 = self.communicator().createObjectAdapter("IqOut")
        #audio_adapter_2 = self.communicator().createObjectAdapter("StreamOut")
        audio_adapter_2.add(IqOutI(ozy_impl), self.communicator().stringToIdentity("iqout"))       
        # Now we can receive callbacks for tx audio 
        audio_adapter_2.activate()
        
        #-----------------------------------------------------------------------
        # One way audio channel out - streamin refers to the receiver
        stream = ice_helpers.get_interface('Ozy Service', self.communicator, AcornIqIn.IqInPrx, 'IqIn.Proxy')
        streamOneway = AcornIqIn.IqInPrx.uncheckedCast(stream.ice_oneway())
        
        print 'Acorn-SDR (Ozy Service) running..'
        
        #-----------------------------------------------------------------------
        # main loop
        while not self.close:
            # Need a break out of this loop for a close
            try:
                # Get the next audio block and dispatch
                samples = ozy_impl.capture()
                # Use for non-threaded
                #if samples:
                # Use for threaded
                if (type(samples) == TupleType):
                    stream.data(samples)
                ozy_impl.write()
                ozy_impl.apply_updates()
            except IOError, e:
                # Ignore
                pass
            except Ice.CommunicatorDestroyedException, e:
                # Ignore
                pass
            #sleep(0.001)
        
        #-----------------------------------------------------------------------
        # Wait for closedown
        while not self.close:
            # Idle loop for doing any other work
            sleep(1)

        # Unsubscribe all subscribed objects.
        # Seems to cause issues
        #ozy_topic.unsubscribe(subscriber_1)
        #life_cycle_topic.unsubscribe(subscriber_2)
    
    #---------------------------------------------------------------------------
    # Terminate
    def terminate(self):
        self.close = True
        
#===============================================================================
#===============================================================================
# Start from the command line
if __name__ == "__main__":
   
    app = OzyService()
    app.main(sys.argv, sys.argv[1])
  
    # Announce we are going 
    print 'Acorn-SDR  (Ozy Service) closing..'




